// -*- C++ -*- 

#include "Parameter.h"
#include "Parser.h"
#include <cstdlib>
#include <cstdio>
#include <cstdarg>
#include <ctype.h>
#include <sstream>


//------------------------------------------------------------------------------

Parameter::Parameter(const char* n,int m):moduleTag(m) { 
  isAsgn = false; 
  name = n; 
  tag = ""; 
}

//------------------------------------------------------------------------------

Parameter::Parameter(const Parameter& a) {
  isAsgn = a.isAsgn;
  name = a.name;
  tag = a.tag;
  moduleTag = a.moduleTag;
}

//------------------------------------------------------------------------------

Parameter::~Parameter() {}

//------------------------------------------------------------------------------

bool Parameter::isActive() const {
  return Parser::instance().moduleIsActive(moduleTag);
}

//------------------------------------------------------------------------------

void Parameter::assignInteger(int v) {
  fprintf(stderr, "Error: '%s' cannot be assigned an integer\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignDouble(double v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a double\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignString(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a string\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignToken(int v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a token\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignIntegerList(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a integer list\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignDoubleList(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a double list\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignStringList(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a string list\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignTokenList(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned a token list\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

void Parameter::assignIntegerRange(const char *v) {
  fprintf(stderr, "Error: '%s' cannot be assigned an integer range\n", getName());
  exit(1);
}

//------------------------------------------------------------------------------

int Parameter::getOneInteger() const {
  fprintf(stderr, "Error: '%s' cannot return an integer\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

double Parameter::getOneDouble() const {
  fprintf(stderr, "Error: '%s' cannot return a double\n", getName());
  exit(1);  
}


//------------------------------------------------------------------------------

const char* Parameter::getOneString() const {
  fprintf(stderr, "Error: '%s' cannot return a string\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

const char* Parameter::getOneToken() const {
  fprintf(stderr, "Error: '%s' cannot return a token\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

vector<int> Parameter::getOneIntegerList() const {
  fprintf(stderr, "Error: '%s' cannot return an integer list\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

vector<double> Parameter::getOneDoubleList() const {
  fprintf(stderr, "Error: '%s' cannot return a double list\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

vector<string> Parameter::getOneStringList() const {
  fprintf(stderr, "Error: '%s' cannot return a string list\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

vector<string> Parameter::getOneTokenList() const {
  fprintf(stderr, "Error: '%s' cannot return a token list\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

vector<pair<int,int> > Parameter::getOneIntegerRange() const {
  fprintf(stderr, "Error: '%s' cannot return an integer range\n", getName());
  exit(1);  
}

//------------------------------------------------------------------------------

Parameter* Parameter::getObject(int n) {
  fprintf(stderr, "Error: '%s' does not have this subtoken\n", getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

const Parameter* Parameter::getObject(int n) const {
  fprintf(stderr, "Error: '%s' does not have this subtoken\n", getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

Parameter* Parameter::getMultiObject(const char* n) {
  fprintf(stderr, "Error: '%s' is not a multiple parameter\n", getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

void Parameter::addComment(const string& line) {
  comments.push_back(line);
}

#include <iostream>
using std::cout;
using std::endl;

//------------------------------------------------------------------------------

void Parameter::dumpWithComments(FILE* out,const char* prefix) const {
  vector<string>::const_iterator cIter = comments.begin();
  for (;cIter!=comments.end();++cIter) fprintf(out,"%s// %s\n",prefix,cIter->c_str());
  dump(out,prefix);
}

//------------------------------------------------------------------------------

ClassParameter::ClassParameter(const char* n,int m) : Parameter(n,m) {
}

//------------------------------------------------------------------------------

ClassParameter::~ClassParameter() {
}

//------------------------------------------------------------------------------

ClassParameter::ClassParameter(const ClassParameter& cp) : Parameter(cp) {
  for (size_t i=0; i<cp.subAsgn.size(); ++i) {
    subAsgn.push_back(cp.subAsgn[i]->duplicate());
    subToken.push_back(cp.subToken[i]);
  }
}

//------------------------------------------------------------------------------

Parameter* ClassParameter::duplicate() const {
  return new ClassParameter(*this);
}

//------------------------------------------------------------------------------

Parameter* ClassParameter::addObject(Parameter* a) {
  const char* text = a->getName();
  // cout<< "Merging " << text << " with " << this->getName() << " - " << a << " vs " << this <<  endl;
  if (text && text[0]) {
    if (isupper(text[0]) == 0) {
      fprintf(stderr, "Error: token '%s' must start with a capital letter in '%s'\n", 
              text, getName());
      exit(1);
    }
  }
  else {
    fprintf(stderr, "Error: invalid empty token in '%s'\n", getName());
    exit(1);
  }
  int nobjs = subAsgn.size();
  for (int i=0; i<nobjs; ++i) {
    if (strcmp(text, subAsgn[i]->getName()) == 0) {
      
      if (subAsgn[i]->merge(a)) return subAsgn[i];
      else{
        fprintf(stderr, "Error: token '%s' already used in '%s'\n", text, getName());
        exit(1);
      }
    }
  }
  subToken.push_back(Parser::instance().createDictionaryToken(text));
  subAsgn.push_back(a);
  return a;
}

//------------------------------------------------------------------------------

Parameter* ClassParameter::getObject(int t) {
  for (size_t i=0; i<subToken.size(); ++i) {
    if (subToken[i] == t) return subAsgn[i];
  }
  fprintf(stderr, "Error: token '%s' not found in '%s'\n", 
          Parser::instance().getDictionaryWord(t), getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

const Parameter* ClassParameter::getObject(int t) const {
  for (size_t i=0; i<subToken.size(); ++i) {
    if (subToken[i] == t) return subAsgn[i];
  }
  fprintf(stderr, "Error: token '%s' not found in '%s'\n", 
          Parser::instance().getDictionaryWord(t), getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

Parameter* ClassParameter::getMultiObject(const char* n) {
  if (strcmp(n, "") != 0) {
    fprintf(stderr, "Error: '%s' is not a multiple parameter\n", getName());
    exit(1);
  }
  return this;
}

//------------------------------------------------------------------------------

ClassParameter& ClassParameter::add(const char* n,int m) { //= addObject but with != args
  ClassParameter* a = new ClassParameter(n,m);
  a = dynamic_cast<ClassParameter*> (addObject(a));
  if (!a) exit(1);
  return *a;
}

//------------------------------------------------------------------------------

ClassParameter& ClassParameter::addMultiple(const char* n,int m) {
  ClassParameter* a = new MultiClassParameter(n,m);
  a = dynamic_cast<MultiClassParameter*> (addObject(a));
  if (!a) exit(1);
  return *a;
}

//------------------------------------------------------------------------------

void ClassParameter::addInteger(const char* n, int v,int m) {
  addObject(new IntegerParameter(n, v,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addDouble(const char* n, double v,int m) {
  addObject(new DoubleParameter(n, v,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addString(const char* n, const char* v,int m) {
  addObject(new StringParameter(n, v,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addToken(const char* n, int num, ...) {
  va_list args;
  va_start(args, num);
  vector<string> vtoken;
  for (int i=0; i<num+1; ++i) vtoken.push_back(va_arg(args, const char*));
  va_end(args);
  addObject(new TokenParameter(n, vtoken));
}


//------------------------------------------------------------------------------

void ClassParameter::addToken(int m,const char* n, int num, ...) {
  va_list args;
  va_start(args, num);
  vector<string> vtoken;
  for (int i=0; i<num+1; ++i) vtoken.push_back(va_arg(args, const char*));
  va_end(args);
  addObject(new TokenParameter(n, vtoken,m));
}


//------------------------------------------------------------------------------

void ClassParameter::addToken(const char* n,const set<string>& val,const string& def,int m) {
  
  vector<string> vtoken;
  if (val.find(def) == val.end()) vtoken.push_back(def);
  set<string>::const_iterator sIter = val.begin();
  for (;sIter!=val.end();++sIter) vtoken.push_back(*sIter);
  vtoken.push_back(def);
  addObject(new TokenParameter(n, vtoken,m));
  
}

//------------------------------------------------------------------------------

void ClassParameter::addIntegerList(const char* n,int m) {
  addObject(new ListParameter<int>(n,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addDoubleList(const char* n,int m) {
  addObject(new ListParameter<double>(n,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addStringList(const char* n,int m) {
  addObject(new ListParameter<string>(n,m));
}

//------------------------------------------------------------------------------

void ClassParameter::addTokenList(const char* n, int num, ...) {
  va_list args;
  va_start(args, num);
  vector<string> vtoken;
  for (int i=0; i<num; ++i) vtoken.push_back(va_arg(args, const char*));
  va_end(args);
  addObject(new TokenListParameter(n, vtoken));
}

//------------------------------------------------------------------------------

void ClassParameter::addTokenList(int m, const char* n, int num, ...) {
  va_list args;
  va_start(args, num);
  vector<string> vtoken;
  for (int i=0; i<num; ++i) vtoken.push_back(va_arg(args, const char*));
  va_end(args);
  addObject(new TokenListParameter(n, vtoken, m));
}


//------------------------------------------------------------------------------

void ClassParameter::addTokenList(const char* n,const set<string>& val,const string& def,int m) {
  
  vector<string> vtoken;
  if (val.find(def) == val.end()) vtoken.push_back(def);
  set<string>::const_iterator sIter = val.begin();
  for (;sIter!=val.end();++sIter) vtoken.push_back(*sIter);
  addObject(new TokenListParameter(n, vtoken, m));
  
}


//------------------------------------------------------------------------------

void ClassParameter::addIntegerRange(const char* n,int m) {
  addObject(new ListParameter<pair<int,int> >(n,m));
}

//------------------------------------------------------------------------------

ClassParameter* ClassParameter::find(const char* n, int pos) {
  ClassParameter* cp = 0;
  // replace the dot by a blank space to ease the scanning
  int len = strlen(n);
  char* arg = new char[len+1];
  strcpy(arg, n);
  int i;
  for (i=0; i<len; ++i) {
    if (arg[i] == '.') arg[i] = ' ';
  }
  // scan and call
  Parameter* a = 0;
  i = 0;
  do {
    if (arg[i] == ' ') {
      ++i;
    } else {
      char str[256];
      sscanf(arg+i, "%s", str);
      if (a) a = a->getObject(Parser::instance().getDictionaryToken(str));
      else   a = getObject(Parser::instance().getDictionaryToken(str));
      i += strlen(str);
    }
  } while (i < len);
  if (arg) delete [] arg;
  // check whether we have a multi parameter
  if (pos >= 0) {
    MultiClassParameter* mcp = dynamic_cast<MultiClassParameter*>(a);
    if (!mcp) {
      fprintf(stderr, "Error: '%s' is not a multiple composite parameter\n", n);
      exit(1);
    }
    cp = mcp->get(pos);
  } else {
    cp = dynamic_cast<ClassParameter*>(a);
  }
  return cp;
}
//------------------------------------------------------------------------------

ClassParameter* ClassParameter::find(const char* n, const char* tag) {
  
  ClassParameter* cp = 0;
  // replace the dot by a blank space to ease the scanning
  int len = strlen(n);
  char* arg = new char[len+1];
  strcpy(arg, n);
  int i;
  for (i=0; i<len; ++i) {
    if (arg[i] == '.') arg[i] = ' ';
  }
  // scan and call
  Parameter* a = 0;
  i = 0;
  do {
    if (arg[i] == ' ') {
      ++i;
    } else {
      char str[256];
      sscanf(arg+i, "%s", str);
      if (a) a = a->getObject(Parser::instance().getDictionaryToken(str));
      else a = getObject(Parser::instance().getDictionaryToken(str));
      i += strlen(str);
    }
  } while (i < len);
  if (arg) delete [] arg;
  // check whether we have a multi parameter
  
  MultiClassParameter* mcp = dynamic_cast<MultiClassParameter*>(a);
  if (!mcp) {
    fprintf(stderr, "Error: '%s' is not a multiple composite parameter\n", n);
    exit(1);
  }
  
  for (int pos=0;pos<mcp->size();pos++) {
    cp = mcp->get(pos);
    if (strcmp(cp->getTag(),tag) == 0) return cp;
  }
  return NULL;
}

//------------------------------------------------------------------------------

IntegerParameter&  ClassParameter::getIntegerParameter(const char* name) const {
  return getParameterTemplate<IntegerParameter>(name);}

//------------------------------------------------------------------------------

DoubleParameter& ClassParameter::getDoubleParameter(const char* name) const {
  return getParameterTemplate<DoubleParameter>(name);}

//------------------------------------------------------------------------------

StringParameter& ClassParameter::getStringParameter(const char* name) const {
  return getParameterTemplate<StringParameter>(name);}

//------------------------------------------------------------------------------

TokenParameter&  ClassParameter::getTokenParameter(const char* name) const {
  return getParameterTemplate<TokenParameter>(name);}

//------------------------------------------------------------------------------

ClassParameter& ClassParameter::get(const char* n, int pos) {
  ClassParameter* cp = find(n,pos);

  if (!cp) {
    if (pos != -1) {
      fprintf(stderr, "Error: Could not find the %d-th instance of parameter '%s' in '%s'\n",
              pos , n, getName());
    }
    else {
      fprintf(stderr, "Error: Could not find parameter '%s' in '%s'\n", n, getName());
    }
    exit(1);
  }
  return *cp;
}
//------------------------------------------------------------------------------

ClassParameter& ClassParameter::get(const char* n, const char* tag) {
  
  ClassParameter* cp = find(n,tag);

  if (!cp) {
    fprintf(stderr,
            "Error: The instance '%s' of parameter '%s' was not found in '%s'\n",
            tag, n, getName());  
    exit(1);
  }
  
  return *cp;
}
//------------------------------------------------------------------------------

ClassParameter& ClassParameter::get(const char* n, const std::string& tag) {
  return get(n,tag.c_str());
}

//------------------------------------------------------------------------------

int ClassParameter::getInteger(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneInteger();
}

//------------------------------------------------------------------------------

double ClassParameter::getDouble(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneDouble();
}

//------------------------------------------------------------------------------

const char* ClassParameter::getString(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneString();
}

//------------------------------------------------------------------------------

const char* ClassParameter::getToken(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneToken();
}

//------------------------------------------------------------------------------

vector<int> ClassParameter::getIntegerList(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneIntegerList();
}

//------------------------------------------------------------------------------

vector<double> ClassParameter::getDoubleList(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneDoubleList();
}

//------------------------------------------------------------------------------

vector<double> ClassParameter::getDoubleVector(const char* n) const {
  vector<double> vec = getObject(Parser::instance().getDictionaryToken(n))->getOneDoubleList();
  if (vec.size() != 3) {
    fprintf(stderr,"\'%s.%s\' is not a vector (it has %d components) --> exiting\n",getName(),n,(int) vec.size());
    exit(1);
  }
  return vec;
}                                 

//------------------------------------------------------------------------------

vector<string> ClassParameter::getStringList(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneStringList();
}

//------------------------------------------------------------------------------

vector<string> ClassParameter::getTokenList(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneTokenList();
}

//------------------------------------------------------------------------------

vector<pair<int,int> > ClassParameter::getIntegerRange(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->getOneIntegerRange();
}

//------------------------------------------------------------------------------

bool ClassParameter::isAssigned() const {

  vector<Parameter*>::const_iterator pIter = subAsgn.begin();
  vector<Parameter*>::const_iterator pLast = subAsgn.end();

  for (;pIter!=pLast;++pIter) if ((*pIter)->isAssigned()) return true;
  
  return false;
}

//------------------------------------------------------------------------------

bool ClassParameter::isAssigned(const char* n) const {
  return getObject(Parser::instance().getDictionaryToken(n))->isAssigned();
}

//------------------------------------------------------------------------------

int ClassParameter::size() const {
  fprintf(stderr, "Error: function size not allowed for non multiple '%s'\n",
          getName());
  exit(1);
  return -1;
}

//------------------------------------------------------------------------------

void ClassParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    string t;
    if (tag != "") { t += " = "; t += "\""; t += tag; t += "\""; }
    else t = "";
    fprintf(fp, "%s%s%s {\n", prefix, getName(), t.c_str());
    string subprefix = "  ";
    subprefix += prefix;
    for (size_t i=0; i<subAsgn.size(); ++i) subAsgn[i]->dumpWithComments(fp, subprefix.c_str());
    fprintf(fp, "%s}\n", prefix);
  }
}

//------------------------------------------------------------------------------

void ClassParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive() && isAssigned()) {
    string t;
    if (tag != "") { t += " = "; t += "\""; t += tag; t += "\""; }
    else t = "";

    out<< prefix << getName() << t.c_str() << " {\n";
    // fprintf(fp, "%s%s%s {\n", prefix, getName(), t.c_str());
    string subprefix = "  ";
    subprefix += prefix;
    for (size_t i=0; i<subAsgn.size(); ++i) subAsgn[i]->dumpAssigned(out, subprefix.c_str());
    out << prefix << "}\n";
    // fprintf(fp, "%s}\n", prefix);
  }
}

//------------------------------------------------------------------------------

MultiClassParameter::MultiClassParameter(const char* n,int m) : ClassParameter(n,m) {
}

//------------------------------------------------------------------------------

MultiClassParameter::~MultiClassParameter() {
}

//------------------------------------------------------------------------------

MultiClassParameter::MultiClassParameter(const MultiClassParameter& mcp) : 
  ClassParameter(mcp) {
  for (size_t i=0; i<mcp.list.size(); ++i) {
    list.push_back(new ClassParameter(*mcp.list[i]));
  }
}

//------------------------------------------------------------------------------

Parameter* MultiClassParameter::duplicate() const {
  return new MultiClassParameter(*this);
}

//------------------------------------------------------------------------------

Parameter* MultiClassParameter::getMultiObject(const char* n) {
  if (strcmp(n, "") != 0) {
    for (size_t i=0; i<list.size(); ++i) {
      if (strcmp(n, list[i]->getTag()) == 0) {
        fprintf(stderr, "Info: reusing '%s' with tag '%s'\n", getName(), n);
        return list[i];
      }
    }
  }
  ClassParameter* cp = new ClassParameter(*this);
  cp->setTag(n);
  list.push_back(cp);
  return cp;
}

//------------------------------------------------------------------------------

ClassParameter* MultiClassParameter::get(int pos) {
  return list[pos];
}

//------------------------------------------------------------------------------

int MultiClassParameter::getInteger(const char* n) const {
  fprintf(stderr, "Error: function getInteger not allowed for multiple '%s'\n", 
          getName());
  exit(1);
  return 0;
}

//------------------------------------------------------------------------------

double MultiClassParameter::getDouble(const char* n) const {
  fprintf(stderr, "Error: function getDouble not allowed for multiple '%s'\n", 
          getName());
  exit(1);
  return 0.0;
}

//------------------------------------------------------------------------------

const char* MultiClassParameter::getString(const char* n) const {
  fprintf(stderr, "Error: function getString not allowed for multiple '%s'\n", 
          getName());
  exit(1);
  return "";
}

//------------------------------------------------------------------------------

const char* MultiClassParameter::getToken(const char* n) const {
  fprintf(stderr, "Error: function getToken not allowed for multiple '%s'\n", 
          getName());
  exit(1);
  return "";
}

//------------------------------------------------------------------------------

vector<int> MultiClassParameter::getIntegerList(const char* n) const {
  fprintf(stderr, "Error: function getIntegerList not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

vector<double> MultiClassParameter::getDoubleList(const char* n) const {
  fprintf(stderr, "Error: function getDoubleList not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

vector<double> MultiClassParameter::getDoubleVector(const char* n) const {
  fprintf(stderr, "Error: function getDoubleVector not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

vector<string> MultiClassParameter::getStringList(const char* n) const {
  fprintf(stderr, "Error: function getStringList not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

vector<string> MultiClassParameter::getTokenList(const char* n) const {
  fprintf(stderr, "Error: function getTokenList not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

vector<pair<int,int> > MultiClassParameter::getIntegerRange(const char* n) const {
  fprintf(stderr, "Error: function getIntegerRange not allowed for multiple '%s'\n", 
          getName());
  exit(1);
}

//------------------------------------------------------------------------------

bool MultiClassParameter::isAssigned() const {
  
  vector<ClassParameter*>::const_iterator pIter = list.begin();
  vector<ClassParameter*>::const_iterator pLast = list.end();

  for (;pIter!=pLast;++pIter) if ((*pIter)->isAssigned()) return true;
  
  return false;
}


//------------------------------------------------------------------------------

bool MultiClassParameter::isAssigned(const char* n) const {
  
  fprintf(stderr, "Error: function isAssigned not allowed for multiple '%s'\n", 
          getName());
  exit(1);
  return false;
  
}

//------------------------------------------------------------------------------

int MultiClassParameter::size() const { 
  return list.size(); 
}

//------------------------------------------------------------------------------

void MultiClassParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    for (size_t i=0; i<list.size(); ++i) list[i]->dump(fp, prefix);
    if (list.size() == 0) {
      fprintf(fp, "%s// unassigned multiple parameter\n", prefix);
      ClassParameter::dump(fp, prefix);
    }
  }
}

//------------------------------------------------------------------------------

void MultiClassParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive() && isAssigned()) {
    for (size_t i=0; i<list.size(); ++i) list[i]->dumpAssigned(out, prefix);
    if (list.size() == 0) {
      out << prefix << "// unassigned multiple parameter\n";
      // fprintf(fp, "%s// unassigned multiple parameter\n", prefix);
      ClassParameter::dumpAssigned(out, prefix);
    }
  }
}

//------------------------------------------------------------------------------

IntegerParameter::IntegerParameter(const char* n, int v, int m) : Parameter(n,m) {
  val = v;
}

//------------------------------------------------------------------------------

IntegerParameter::~IntegerParameter() {
}

//------------------------------------------------------------------------------

IntegerParameter::IntegerParameter(const IntegerParameter& a) : Parameter(a) {
  val = a.val;
}

//------------------------------------------------------------------------------

Parameter* IntegerParameter::duplicate() const {
  return new IntegerParameter(*this);
}

//------------------------------------------------------------------------------

void IntegerParameter::assignInteger(int v) { 
  if (isActive()) {
    val = v;
    isAsgn = true;
  }
}

//------------------------------------------------------------------------------

int IntegerParameter::getOneInteger() const { 
  return val;
}

//------------------------------------------------------------------------------

void IntegerParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    fprintf(fp, "%s%s%s = %d;\n", prefix, isAsgn ? "" : "// ", getName(), val);
  }
}

//------------------------------------------------------------------------------

void IntegerParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive() && isAssigned()) {

    out << prefix << getName() << " = " << val << ";\n";
    // fprintf(fp, "%s%s%s = %d;\n", prefix, isAsgn ? "" : "// ", getName(), val);
  }
}

//------------------------------------------------------------------------------

IntegerParameter& IntegerParameter::operator= (int value) {
  val = value;
  isAsgn = true;
  return * this;
}

//------------------------------------------------------------------------------

DoubleParameter::DoubleParameter(const char* n, double v,int m) : Parameter(n,m) {
  val = v;
}

//------------------------------------------------------------------------------

DoubleParameter::DoubleParameter(const DoubleParameter& a) : Parameter(a) {
  val = a.val;
}

//------------------------------------------------------------------------------

DoubleParameter::~DoubleParameter() {
}

//------------------------------------------------------------------------------

Parameter* DoubleParameter::duplicate() const {
  return new DoubleParameter(*this);
}

//------------------------------------------------------------------------------

void DoubleParameter::assignInteger(int v) {
  if (isActive()) {
    val = v;
    isAsgn = true;
  }
}

//------------------------------------------------------------------------------

void DoubleParameter::assignDouble(double v) {
  if (isActive()) {
    val = v;
    isAsgn = true;
  }
}

//------------------------------------------------------------------------------

double DoubleParameter::getOneDouble() const {
  return val;
}

//------------------------------------------------------------------------------

void DoubleParameter::dump(FILE* fp, const char* prefix) const {

  if (isActive()) {
    fprintf(fp, "%s%s%s = %e;\n", prefix, isAsgn ? "" : "// ", getName(), val);
  }
}

//------------------------------------------------------------------------------

void DoubleParameter::dumpAssigned(ostream& out, const char* prefix) const {

  if (isActive() && isAssigned()) {
    out << prefix << getName() << " = " << val << ";\n";
    // fprintf(fp, "%s%s%s = %e;\n", prefix, isAsgn ? "" : "// ", getName(), val);
  }
}

//------------------------------------------------------------------------------

DoubleParameter& DoubleParameter::operator= (double value) {
  val = value;
  isAsgn = true;
  return * this;
}

//------------------------------------------------------------------------------

StringParameter::StringParameter(const char* n, const char* v,int m) : Parameter(n,m) {
  val = v;
}

//------------------------------------------------------------------------------

StringParameter::StringParameter(const StringParameter& a) : Parameter(a) {
  val = a.val;
}

//------------------------------------------------------------------------------

StringParameter::~StringParameter() {
}

//------------------------------------------------------------------------------

Parameter* StringParameter::duplicate() const {
  return new StringParameter(*this);
}

//------------------------------------------------------------------------------

void StringParameter::assignString(const char* v) {
  if (isActive()) {
    val = v;
    isAsgn = true;
  }
}

//------------------------------------------------------------------------------

const char* StringParameter::getOneString() const {
  return val.c_str();
}

//------------------------------------------------------------------------------

void StringParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    fprintf(fp, "%s%s%s = \"%s\";\n", prefix, isAsgn ? "" : "// ", getName(), val.c_str());
  }
}

//------------------------------------------------------------------------------

void StringParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive() && isAssigned()) {
    out << prefix << getName() << " = \"" << val << "\";\n";
    // fprintf(fp, "%s%s%s = \"%s\";\n", prefix, isAsgn ? "" : "// ", getName(), val.c_str());
  }
}

//------------------------------------------------------------------------------

StringParameter& StringParameter::operator= (const string& value) {
  val = value;
  isAsgn = true;
  return * this;
}

//------------------------------------------------------------------------------

TokenParameter::TokenParameter(const char* n, const vector<string>& vtoken,int m)  : 
  Parameter(n,m) {
  int size = vtoken.size();
  val = Parser::instance().createDictionaryToken(vtoken[size-1].c_str()); // assign default
  bool foundDefault = false;
  for (int i=0; i<size-1; ++i) {
    int token = Parser::instance().createDictionaryToken(vtoken[i].c_str());
    tk.push_back(token);
    if (val == token) foundDefault = true;
  }
  if (!foundDefault) {
    fprintf(stderr, "Error: no default value for token '%s'\n", n);
    exit(1);
  }
}

//------------------------------------------------------------------------------

TokenParameter::TokenParameter(const TokenParameter& a) : Parameter(a) {
  val = a.val;
  for (size_t i=0; i<a.tk.size(); ++i) tk.push_back(a.tk[i]);
}

//------------------------------------------------------------------------------

TokenParameter::~TokenParameter() {}

//------------------------------------------------------------------------------

Parameter* TokenParameter::duplicate() const {
  return new TokenParameter(*this);
}

//------------------------------------------------------------------------------

void TokenParameter::assignToken(int t) {
  if (isActive()) {
    for (size_t i=0; i<tk.size(); ++i) {
      if (tk[i] == t) {
        val = t;
        isAsgn = true;
        return;
      }
    }
    
    std::ostringstream allTokens;
    for (size_t i=0;i<tk.size();++i) {
      allTokens << " " << Parser::instance().getDictionaryWord(tk[i]);
    }
      
    fprintf(stderr, "Error: '%s' is not a valid token for '%s' (allowed:%s) \n", 
            Parser::instance().getDictionaryWord(t), getName(),allTokens.str().c_str());
    exit(1);
  }
}

//------------------------------------------------------------------------------

const char* TokenParameter::getOneToken() const {
  return Parser::instance().getDictionaryWord(val);
}

//------------------------------------------------------------------------------

void TokenParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    fprintf(fp, "%s%s%s = %s;", prefix, isAsgn ? "" : "// ", 
            getName(), Parser::instance().getDictionaryWord(val));
    if (!isAsgn) {
      fprintf(fp, " //");
      for (size_t i=0; i<tk.size(); ++i) {
        fprintf(fp, " %s", Parser::instance().getDictionaryWord(tk[i]));
      }
    }
    fprintf(fp, "\n");
  }
}

//------------------------------------------------------------------------------

void TokenParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive()&& isAssigned()) {
    out << prefix << getName() << " = " << Parser::instance().getDictionaryWord(val) << ";\n";
  }
}

//------------------------------------------------------------------------------

TokenParameter& TokenParameter::operator= (const string& value) { 
  int v = Parser::instance().getDictionaryToken(value.c_str());
  vector<int>::iterator tkIter = tk.begin();
  for (;tkIter!=tk.end();++tkIter) {
    if (*tkIter == v) {val = v;break;}
  }
  if (tkIter==tk.end()) throw;
  isAsgn = true;
  return *this;
}

//------------------------------------------------------------------------------

TokenListParameter::TokenListParameter(const char* n, const vector<string>& vtoken,int m) :
  ListParameter<int>(n,m) {
  for (size_t i=0; i<vtoken.size(); ++i) {
    tk.push_back(Parser::instance().createDictionaryToken(vtoken[i].c_str()));
  }
}

//------------------------------------------------------------------------------

TokenListParameter::TokenListParameter(const TokenListParameter& a) : 
  ListParameter<int>(a) {
  for (size_t i=0; i<a.tk.size(); ++i) tk.push_back(a.tk[i]);
}

//------------------------------------------------------------------------------

TokenListParameter::~TokenListParameter() {
}

//------------------------------------------------------------------------------

Parameter* TokenListParameter::duplicate() const {
  return new TokenListParameter(*this);
}

//------------------------------------------------------------------------------

void TokenListParameter::assignTokenList(const char* line) {
  if (isActive()) {
    assignList(line);
  }
}

//------------------------------------------------------------------------------

vector<string> TokenListParameter::getOneTokenList() const {
  vector<string> v;
  for (size_t i=0; i<val.size(); ++i) {
    v.push_back(Parser::instance().getDictionaryWord(val[i]));
  }
  return v;
}

//------------------------------------------------------------------------------

void TokenListParameter::addToList(const char* text) {
  int t = Parser::instance().getDictionaryToken(text);
  for (size_t i=0; i<tk.size(); ++i) {
    if (tk[i] == t) {
      val.push_back(t);
      isAsgn = true;
      return;
    }
  }
  
  std::ostringstream allTokens;
  for (size_t i=0;i<tk.size();++i) {
    allTokens << " " << Parser::instance().getDictionaryWord(tk[i]);
  }
  
  fprintf(stderr, "Error: '%s' is not a valid token for list '%s' (allowed:%s) \n", 
          Parser::instance().getDictionaryWord(t), getName(),allTokens.str().c_str());
  exit(1);

}

//------------------------------------------------------------------------------

string TokenListParameter::convertToString(const int& t) const {
  string s(Parser::instance().getDictionaryWord(t));
  return s;
}

//------------------------------------------------------------------------------

void TokenListParameter::dump(FILE* fp, const char* prefix) const {
  if (isActive()) {
    linedump(fp, prefix);
    if (!isAsgn) {
      int size = tk.size();
      fprintf(fp, " // [");
      for (int i=0; i<size; ++i) {
        fprintf(fp, "%s%s", Parser::instance().getDictionaryWord(tk[i]), 
              i==size-1 ? "" : " ");
      }
      fprintf(fp, "]");
    }
    fprintf(fp, "\n");
  }
}

//------------------------------------------------------------------------------

void TokenListParameter::dumpAssigned(ostream& out, const char* prefix) const {
  if (isActive() && isAssigned()) {
    linedump(out,prefix);
    out << "\n";

//     linedump(fp, prefix);
//     if (!isAsgn) {
//       int size = tk.size();
//       fprintf(fp, " // [");
//       for (int i=0; i<size; ++i) {
//         fprintf(fp, "%s%s", Parser::instance().getDictionaryWord(tk[i]), 
//               i==size-1 ? "" : " ");
//       }
//       fprintf(fp, "]");
//     }
//     fprintf(fp, "\n");
  }
}

// ------------------------------------------------------------------------------

bool Parameter::merge(Parameter* other) {
  return false;
}

//------------------------------------------------------------------------------

bool ClassParameter::merge(Parameter* other) {
  // cout<< "merging class " << other->getName() << endl;
  ClassParameter* tmp = dynamic_cast<ClassParameter*> (other);
  if (tmp != NULL) {delete other; return true;}
  exit(1);
  return false;
}

//------------------------------------------------------------------------------

bool MultiClassParameter::merge(Parameter* other) {
  // cout<< "merging multiclass " << other->getName() << endl;
  MultiClassParameter* tmp = dynamic_cast<MultiClassParameter*> (other);
  if (tmp != NULL) {delete other; return true;}
  exit(1);
  return false;
}

//------------------------------------------------------------------------------

bool IntegerParameter::merge(Parameter* other) {
  // cout<< "merging integer " << other->getName() << endl;
  IntegerParameter* tmp = dynamic_cast<IntegerParameter*> (other);
  if (tmp != NULL) {delete other; return true;}
  exit(1);
  return false;
}

//------------------------------------------------------------------------------

bool DoubleParameter::merge(Parameter* other) {
  // cout<< "merging double " << other->getName() << endl;
  DoubleParameter* tmp = dynamic_cast<DoubleParameter*> (other);
  if (tmp != NULL) {delete other; return true;}
  exit(1);
  return false;
}

//------------------------------------------------------------------------------

bool StringParameter::merge(Parameter* other) {
  // cout<< "merging string " << other->getName() << endl;
  StringParameter* tmp = dynamic_cast<StringParameter*> (other);
  if (tmp != NULL) {delete other; return true;}
  exit(1);
  return false;
}

//------------------------------------------------------------------------------

bool TokenParameter::merge(Parameter* other) {

  // cout<< "merging token " << other->getName() << endl;
  
  TokenParameter* tp = dynamic_cast<TokenParameter*> (other);
  if (!tp) exit(1);

  set<int> tmp;
  tmp.insert(tk.begin(),tk.end());
  for (vector<int>::const_iterator tIter = tp->tk.begin();tIter!=tp->tk.end();++tIter) {
    if (tmp.find(*tIter) == tmp.end()) {
      tk.push_back(*tIter);
      tmp.insert(*tIter);
    }
  }
  
  delete tp;
  return true;
}

//------------------------------------------------------------------------------

bool TokenListParameter::merge(Parameter* other) {
  
  // cout<< "merging token list " << other->getName() << endl;
  TokenListParameter* tp = dynamic_cast<TokenListParameter*>(other);
  if (!tp) exit(1);  
  set<int> tmp;
  tmp.insert(tk.begin(),tk.end());
  for (vector<int>::const_iterator tIter = tp->tk.begin();tIter!=tp->tk.end();++tIter) {
    if (tmp.find(*tIter) == tmp.end()) {
      tk.push_back(*tIter);
      tmp.insert(*tIter);
    }
  }
  delete tp;
  return true;
}
